/** 迷你的json格式生成类
 * @file jsonwriter.hpp
 * @author Kiven Lee
 * @version 1.0
 * @date 2021-08-26
 *
 * @copyright Kivensoft (c) 2018 - 2021
 *
 */
#pragma once

#include <cstdarg>
#include <ctime>
#include <cassert>
#include <string>
#include <string_view>
#include <type_traits>
#include "utils.hpp"
#include "printf.hpp"

namespace klib {
    struct json_time_t {
        time_t time;
        json_time_t(): time(std::time(nullptr)) {}
        json_time_t(time_t time) :time(time) {}
        operator time_t() { return time; }
    };

    /** json字符串生成类，内部使用动态增长的内存块实现，减少内存分配碎片 */
    template<size_t _SIZE = 512>
    class json_writer_t : public dstring_t<_SIZE> {

    protected:
        bool _first;

    public:

        /** 普通字符串转换成json格式字符串(不会自动在头尾添加双引号)
         *
         * @param dst 写入的目标地址，为nullptr时，只计算大小，不做实际转换
         * @param src 源字符串地址
         * @return size_t 写入的大小（或者dst=nullptr时计算需要的空间），不包含'\0'结尾字符
         */
        static size_t to_json_string(char* dst, const char* src) {
            size_t count = 0;
            char c;

            if (dst) {
                for (; (c = *src++); ++dst, ++count) {
                    char c2 = _json_conv(c);
                    if (!c2) {
                        *dst = c;
                    } else {
                        *dst++ = '\\';
                        *dst = c2;
                        ++count;
                    }
                }
            } else {
                for (; (c = *src++); ++count)
                    if (!_json_conv(c)) ++count;
            }

            return count;
        }

        /** 普通字符串转换成json格式字符串(不会自动在头尾添加双引号)
         *
         * @param src 源字符串地址
         * @return size_t 生成的json格式字符串
         */
        static std::string to_json_string(const char* src) {
            size_t len = to_json_string(nullptr, src);
            std::string s;
            s.resize(len);
            to_json_string(s.data(), src);
            return s;
        }

        json_writer_t():_first(true) {}

        /** 开始生成一个对象
         *
         * @param key key为空字符串时，不写入键名
         */
        void begin_object(const std::string_view& key) { _begin_obj_arr(key.data(), key.length(), '{'); }

        /** 开始生成一个对象 */
        void begin_object() { _begin_obj_arr(nullptr, 0, '{'); }

        /** 结束对象生成 */
        void end_object() { this->putchar('}'); _first = false; }

        /** 开始生成一个数组
         *
         * @param key key为空时不写入键名
         */
        void begin_array(const std::string_view& key) { _begin_obj_arr(key.data(), key.length(), '['); }

        /** 开始生成一个数组 */
        void begin_array() { _begin_obj_arr(nullptr, 0, '['); }

        /** 结束数组 */
        void end_array() { this->putchar(']'); _first = false; }

        /** 写入键值对 */
        void kv(const std::string_view& key, int64_t val) { _write_kv(key, val); }
        void kv(const std::string_view& key, uint64_t val) { _write_kv(key, val); }
        void kv(const std::string_view& key, int32_t val) { _write_kv(key, (int64_t) val); }
        void kv(const std::string_view& key, uint32_t val) { _write_kv(key, (uint64_t) val); }
        void kv(const std::string_view& key, double val) { _write_kv(key, val); }
        void kv(const std::string_view& key, float val) { _write_kv(key, (double) val); }
        void kv(const std::string_view& key, bool val) { _write_kv(key, val); }
        void kv(const std::string_view& key, const char* val) { _write_kv(key, std::string_view(val)); }
        void kv(const std::string_view& key, const std::string_view& val) { _write_kv(key, val); }
        void kv(const std::string_view& key, std::tm *val) { _write_kv(key, val); }
        void kv(const std::string_view& key, json_time_t val) { _write_kv(key, val); }

        /** 写入键值对, 值为null */
        void kv(const std::string_view& key) {
            if (!_first) this->putchar(',');
            _write_key(key.data(), key.length());
            _write_data(nullptr, 0);
            _first = false;
        }

        /** 写入键值对 */
        void kv_time(const std::string_view& key, std::time_t val) {
            if (!_first) this->putchar(',');
            _write_key(key.data(), key.length());
            _append_time(val);
            _first = false;
        }

        /** 写入键值对(调用用户自定义回调函数，直到回调函数返回长度为0表示结束) */
        template<typename T>
        inline void kv_func(const std::string_view& key, T* arg, std::string_view(*callback)(T* arg)) {
            if (!_first) this->putchar(',');
            _write_key(key.data(), key.length());
            _append_callback((void*) arg, (std::string_view(*)(void* arg)) callback);
            _first = false;
        }

        /** 写入键值对
         *
         * @param key 键名
         * @param fmt 值或者格式化字符串
         * @param args 格式化的不定参数
         */
        template<typename... ARGS>
        inline void kv_print(const std::string_view& key, const char* fmt, ARGS&&... args) {
            if constexpr(sizeof...(args)) {
                va_list va;
                va_start(va, fmt);
                kv_vprint(key, fmt, va);
                va_end(va);
            } else {
                _write_kv(key, fmt);
            }
        }

        /** 写入键值对(字符串)
         *
         * @tparam ARGS 模板类型
         * @param key 键名
         * @param fmt  格式化字符串
         * @param args 参数
         */
        void kv_vprint(const std::string_view& key, const char* fmt, va_list va) {
            if (!_first) this->putchar(',');
            _write_key(key.data(), key.length());
            _append_value(fmt, va);
            _first = false;
        }

        /** 批量写入键值对 */
        template<typename K, typename V, typename... ARGS>
        inline void kvs(K&& key, V&& val, ARGS&&... args) {
            static_assert(sizeof...(args) % 2 == 0, "args count must be even numbers");
            static_assert(std::is_convertible_v<K, std::string_view>, "key can't convert to std::string_view");
            kv(std::forward<K>(key), std::forward<V>(val));
            if constexpr (sizeof...(args)) kvs(std::forward<ARGS>(args)...);
        }

        /** 写入键值对，值为数组 */
        template<typename... ARGS>
        inline void object(const std::string_view& key, ARGS&&... args) {
            begin_object(key);
            kvs(std::forward<ARGS>(args)...);
            end_object();
        }

        template<typename... ARGS>
        inline void array(const std::string_view& key, ARGS&&... args) {
            begin_array(key);
            avs(std::forward<ARGS>(args)...);
            end_array();
        }

        /** 写入数组的单个值 */
        void av(int64_t val) { _write_av(val); }
        void av(uint64_t val) { _write_av(val); }
        void av(int32_t val) { _write_av((int64_t) val); }
        void av(uint32_t val) { _write_av((uint64_t) val); }
        void av(float val) { _write_av((double) val); }
        void av(double val) { _write_av(val); }
        void av(bool val) { _write_av(val); }
        void av(const std::string_view& val) { _write_av(val); }
        void av(std::tm* val) { _write_av(val); }
        void av(json_time_t val) { _write_av(val); }

        /** 写入数组的单个值 */
        void av_time(const std::time_t val) {
            if (!_first) this->_putchar(',');
            _append_time(val);
            _first = false;
        }

        /** 写入数组的单个值 */
        template<typename... ARGS>
        inline void av_print(const char* fmt, ARGS&&... args) {
            if constexpr (sizeof...(args)) {
                va_list va;
                va_start(va, fmt);
                av_vprint(fmt, va);
                va_end(va);
            } else
                _write_av(fmt);
        }

        /** 格式化写入数组的单个值 */
        void av_vprint(const char* fmt, va_list va) {
            if (!_first) this->_putchar(',');
            _append_value(fmt, va);
            _first = false;
        }

        /** 写入多个值 */
        template<typename V, typename... ARGS>
        inline void avs(V&& val, ARGS&&... args) {
            av(std::forward<V>(val));
            if constexpr (sizeof...(args))
                avs(std::forward<ARGS>(args)...);
        }

    protected:
        void _write_json_string(const char* src, size_t len) {
            if (len) {
                assert(src);
                this->putchar('"');
                for (; len; ++src, --len) {
                    char c = *src;
                    char c2 = _json_conv(*src);
                    if (c2 == '\0')
                        this->putchar(c);
                    else {
                        this->putchar('\\');
                        this->putchar(c2);
                    }
                }
                this->putchar('"');
            } else {
                this->write("null", 4);
            }
        }

        void _write_key(const char* key, size_t len) {
            assert(key && len);
            _write_json_string(key, len);
            this->putchar(':');
        }

        void _write_data(const char* data, std::size_t size) {
            if (size) {
                assert(data);
                this->write(data, size);
            } else {
                this->write("null", 4);
            }
        }

        // 开始一个对象或者数组，两者的不同在于参数c，对象传入'{'，数组传入'['
        void _begin_obj_arr(const char* key, const size_t len, const char c) {
            if (!_first) this->putchar(',');

            if (len) {
                assert(key);
                _write_key(key, len);
            }

            this->putchar(c);
            _first = true;
        }

        template<typename T>
        inline void _write_kv(const std::string_view& key, T val) {
            if (!_first) this->putchar(',');

            _write_key(key.data(), key.length());
            _append_value(val);
            _first = false;
        }

        template<typename T>
        inline void _write_av(T val) {
            if (!_first) this->_putchar(',');
            _append_value(val);
            _first = false;
        }

        void _append_value(int64_t val) { _append_uint(val, true); }

        void _append_value(uint64_t val) { _append_uint(val, false); }

        void _append_value(double val) {
            char buf[32];
            int len = klib::snprintf(buf, sizeof(buf), "%g", val);
            if (len != -1) _write_data(buf, len);
        }

        void _append_value(bool val) {
            if (val) _write_data("true", 4);
            else _write_data("false", 5);
        }

        void _append_value(const std::string_view& val) {
            _write_json_string(val.data(), val.length());
        }

        void _append_value(std::tm* val) {
            char buf[32];
            int c = klib::sprintf(buf, R"("%04d-%02d-%02d %02d:%02d:%02d")",
                    val->tm_year + 1900, val->tm_mon + 1, val->tm_mday,
                    val->tm_hour, val->tm_min, val->tm_sec);
            _write_data(buf, c);
        }

        void _append_value(json_time_t val) {
            char buf[32];
            int c = klib::sprintf(buf, R"("%T")", val.time);
            _write_data(buf, c);
        }

        void _append_value(const char* fmt, va_list va) {
            this->_putchar('"');

            klib::vfctprintf([] (char c, json_writer_t* arg) {
                char c2 = _json_conv(c);
                if (!c2)
                    arg->putchar(c);
                else {
                    arg->putchar('\\');
                    arg->putchar(c2);
                }
            }, this, fmt, va);

            this->putchar('"');
        }

        void _append_uint(uint64_t val, bool sym) {
            char buf[32];
            int len = klib::snprintf(buf, sizeof(buf), sym ? "%lld" : "%llu", val);
            if (len) this->write(buf, len);
        }

        void _append_time(std::time_t val) {
            char buf[32];
            int len = klib::snprintf(buf, sizeof(buf), R"("%T")", val);
            this->write(buf, len);
        }

        void _append_callback(void* arg, std::string_view(*callback) (void* arg)) {
            for (auto r = callback(arg); r.length(); r = callback(arg)) {
                assert(r.data());
                this->write(r.data(), r.length());
            }
        }

        static inline const char _JSON_CHARS[] = {
            '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 'b',  't',  'n',  '\0', 'f',  'r', '\0', '\0',
            '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
            '\0', '\0', '"' };

        static inline char _json_conv(char c) {
            return c >= (char) sizeof(_JSON_CHARS) ? (c != '\\' ? '\0' : '\\') : _JSON_CHARS[(int)c];
        }
    };

} // namespace klib